package com.derbysoft.dswitch.adapter.ihg.dswitch3.service.impl;

import com.derbysoft.dswitch.adapter.ihg.common.domain.Status;
import com.derbysoft.dswitch.adapter.ihg.common.log.LogBody;
import com.derbysoft.dswitch.adapter.ihg.common.log.PerformanceLogger;
import com.derbysoft.dswitch.adapter.ihg.common.repository.ReservationRepository;
import com.derbysoft.dswitch.adapter.ihg.common.utils.EnvironmentUtils;
import com.derbysoft.dswitch.adapter.ihg.dswitch3.HotelProviderService;
import com.derbysoft.dswitch.adapter.ihg.dswitch3.translate.Translator;
import com.derbysoft.dswitch.adapter.ihg.dto.OTAHotelAvailRQ;
import com.derbysoft.dswitch.adapter.ihg.dto.OTAHotelAvailRS;
import com.derbysoft.dswitch.adapter.ihg.dto.RoomRate;
import com.derbysoft.dswitch.adapter.ihg.exception.InvokeProviderSideException;
import com.derbysoft.dswitch.adapter.ihg.webservice.handler.WebServiceHandler;
import com.derbysoft.dswitch.adapter.ihg.webservice.impl.AccountProvider;
import com.derbysoft.dswitch.dto.common.KeyValue;
import com.derbysoft.dswitch.dto.hotel.reservation.ReservationRoomStayDTO;
import com.derbysoft.dswitch.remote.hotel.dto.ErrorDTO;
import com.derbysoft.dswitch.remote.hotel.dto.HotelReservationRequest;
import com.derbysoft.dswitch.remote.hotel.dto.HotelReservationResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StopWatch;

/**
 * Created by: jason
 * Date: 2012-05-17
 */
@Service("hotelPreviewService")
public class HotelPreviewServiceImpl extends BaseHotelService
        implements HotelProviderService<HotelReservationRequest, HotelReservationResponse> {

    @Autowired
    private Translator<HotelReservationRequest, OTAHotelAvailRQ, Void> hotelPreviewRQTranslatorV2;

    @Autowired
    private Translator<OTAHotelAvailRS, HotelReservationResponse, HotelReservationRequest> hotelPreviewRSTranslatorV2;

    @Autowired
    @Qualifier("availWebServiceHandler")
    private WebServiceHandler<OTAHotelAvailRQ, OTAHotelAvailRS> hotelAvailWebService;

    @Autowired
    private ReservationRepository reservationRepository;

    @Override
    public HotelReservationResponse invoke(HotelReservationRequest reservationRequest) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        try {
            OTAHotelAvailRQ otaHotelAvailRQ = hotelPreviewRQTranslatorV2.translate(reservationRequest, null);

            OTAHotelAvailRS otaHotelAvailRS = hotelAvailWebService.invoke(otaHotelAvailRQ, true,
                    AccountProvider.of(EnvironmentUtils.getGlobalIhgAvailabilityUrl(), loadAuthorization(reservationRequest.getHeader().getSource())));
            stopWatch.stop();
            long webserviceTime = stopWatch.getTotalTimeMillis();
            saveReservation(reservationRequest);
            if (otaHotelAvailRS.getErrors() != null || !findAvailRoomStay(otaHotelAvailRS, reservationRequest)) {
                updateStatus(reservationRequest.getHotelReservationRQ().getErsp(), false);
                return noAllotmentResponse(otaHotelAvailRS, reservationRequest, String.valueOf(webserviceTime));
            }
            updateStatus(reservationRequest.getHotelReservationRQ().getErsp(), true);
            HotelReservationResponse previewResponse = hotelPreviewRSTranslatorV2.translate(otaHotelAvailRS, reservationRequest);
            previewResponse.getElapsedTimes().add(new KeyValue(ElapsedTimeType.BEFORE_RESERVATION_AVAIL.name(), String.valueOf(webserviceTime)));
            return previewResponse;
        } catch (Exception ex) {
            if (stopWatch.isRunning()) {
                stopWatch.stop();
            }
            long webserviceTime = stopWatch.getTotalTimeMillis();
            HotelReservationResponse failedResponse = failedResponse(reservationRequest, ex);
            failedResponse.getElapsedTimes().add(new KeyValue(ElapsedTimeType.BEFORE_RESERVATION_AVAIL.name(), String.valueOf(webserviceTime)));
            PerformanceLogger.append(LogBody.ERROR_CODE, failedResponse.getError().getCode());
            return failedResponse;
        }
    }

    private boolean findAvailRoomStay(OTAHotelAvailRS otaHotelAvailRS, HotelReservationRequest request) {
        if (otaHotelAvailRS.getRoomStays() == null
                || CollectionUtils.isEmpty(otaHotelAvailRS.getRoomStays().getRoomStay())
                || otaHotelAvailRS.getRoomStays().getRoomStay().get(0) == null
                || otaHotelAvailRS.getRoomStays().getRoomStay().get(0).getRoomRates() == null
                || CollectionUtils.isEmpty(otaHotelAvailRS.getRoomStays().getRoomStay().get(0).getRoomRates().getRoomRate())) {
            return false;
        }
        for (RoomRate roomRate : otaHotelAvailRS.getRoomStays().getRoomStay().get(0).getRoomRates().getRoomRate()) {
            ReservationRoomStayDTO roomStayDTO = request.getHotelReservationRQ().getHotelReservation().getRoomStaysList().get(0);
            if (roomRate.getRatePlanCode().equals(roomStayDTO.getRatePlanCode())
                    && roomRate.getRoomTypeCode().equals(roomStayDTO.getRoomTypeCode() + roomStayDTO.getRatePlanCode())) {
                return true;
            }
        }
        return false;
    }

    private HotelReservationResponse failedResponse(HotelReservationRequest request, Exception ex) {
        HotelReservationResponse response = new HotelReservationResponse();
        response.setTaskId(request.getHeader().getTaskId());
        ErrorDTO errorDTO = new ErrorDTO();
        errorDTO.setCode(SYSTEM);
        errorDTO.setSource(ex instanceof InvokeProviderSideException ? provider : adapter);
        errorDTO.setMessage(ex.getMessage());
        response.setError(errorDTO);
        return response;
    }

    private void updateStatus(String ersp, boolean success) {
        reservationRepository.updateStatus(ersp, success ? Status.PREVIEW : Status.PREVIEW_FAILED);
    }

    private void saveReservation(HotelReservationRequest reservationRequest) {
        boolean existReservation = reservationRepository.existReservation(reservationRequest.getHotelReservationRQ().getErsp());
        if (!existReservation) {
            reservationRepository.create(reservationRequest.getHotelReservationRQ().getErsp(),
                    reservationRequest.getHotelReservationRQ().getHotelReservation().getHotelCode(), Status.PREVIEW.name());
        }
    }
}
